/*
 * p2sp.h
 *
 *  Created on: 2009-10-31
 *      Author: root
 */

#ifndef P2SPSTREAM_H_
#define P2SPSTREAM_H_

#include "packet.h"
#include "app.h"
#include "timer-handler.h"
#include "ns-process.h"
#include "udp-p2sp.h"

#define BROADCAST_ID 0xffffffff
#define TIME_FORMAT "%.15g"
#define CURRENT_TIME Scheduler::instance().clock()
#define CHKBUFSIZE 256

//chunk buf structure

typedef struct chunkbuf {
	uint16_t chunkid;
	uint8_t hasIt;
	uint8_t grouphasIt;
	uint8_t priority;
	struct chunkbuf *prev;
	struct chunkbuf *next;
} chunkbuf_t;

typedef enum strategy {
	GREEDY = 1,
	RAREST_FIRST = 2,
	MOST_WANTED_GR = 3,
	MOST_WANTED_RF = 4,
	MOST_WANTED_RD = 5,
	RD = 6,
	MIXED = 7,
	GREEDY_RD = 8,
	RAREST_FIRST_RD = 9,
} strategy_t;

//p2sp packet types
typedef enum p2sp_state {
  IDLE = 1,
  BUSY = 2,
} p2sp_state_t;

typedef enum peer_tx {
  RANDOM = 1,
  PENDING = 2,
}peer_tx_t;

typedef struct framelist {
	u_int32_t int_time;
	u_int32_t frame_size;
	u_int32_t frame_type;
	//u_int32_t frag_size;

} framelist_t;

class p2spStreamSrvr;
class p2spStreamClient;


//Timer Definition
//Do I need any timer ?
class ReqTimer : public TimerHandler {
  public:
      ReqTimer(p2spStreamClient* t) : TimerHandler(), t_(t) {deadline = 0; remain_time = -1;}
      inline virtual void expire(Event*);
      void freeze();
      void resume();
      void set(double);
      void reset(double);
  protected:
      p2spStreamClient* t_;
      double remain_time;
      double deadline;
};

class SrvrReqTimer : public TimerHandler {
  public:
      SrvrReqTimer(p2spStreamClient* t) : TimerHandler(), t_(t) {}
      inline virtual void expire(Event*);
      void set(double);
      void reset(double);
  protected:
      p2spStreamClient* t_;


};

class ChunkMapReqTimer : public TimerHandler {
  public:
	  ChunkMapReqTimer(p2spStreamClient* t) : TimerHandler(), t_(t) {}
      inline virtual void expire(Event*);
      void set(double);
      void reset(double);
  protected:
      p2spStreamClient* t_;


};


class DataTxTimer : public TimerHandler {
  public:
      DataTxTimer(p2spStreamClient* t) : TimerHandler(), t_(t) {deadline = 0; remain_time = -1;}
      inline virtual void expire(Event*);
      void freeze();
      void resume();
      void set(double);
      void reset(double);
  protected:
      p2spStreamClient* t_;
      double remain_time;
      double deadline;

};

//AppData struct for p2sp App
/*class p2spAppData : public AppData {
public:
        p2sp_t type_;         //packet type?
        uint16_t chunk_id_;    //chunk id
        uint32_t req_bmp_[MAXBMPS];  //request bitmap put into appdata
        int nbytes_;   //bytes for p2sp pkt
        nsaddr_t src_id_;
        nsaddr_t dst_id_;
        int seqno_;

public:
        p2spAppData() : AppData(P2SP_DATA) {}

        inline p2sp_t& type() { return type_; }
        virtual int size() const { return sizeof(p2spAppData); }
        virtual AppData* copy() { return (new p2spAppData(*this)); }
};*/


class StreamSrvrPlayTimer : public TimerHandler {
  public:
	  StreamSrvrPlayTimer(p2spStreamSrvr* t) : TimerHandler(), t_(t) {}
      inline virtual void expire(Event*);
  protected:
      p2spStreamSrvr* t_;


};

class StreamClientPlayTimer : public TimerHandler {
  public:
	  StreamClientPlayTimer(p2spStreamClient* t) : TimerHandler(), t_(t) {}
      inline virtual void expire(Event*);
  protected:
      p2spStreamClient* t_;


};


//P2SP Client Application Class Definition
class p2spStreamClient : public Application {
  public:
        p2spStreamClient();
        void send_srvr_req();
        void send_peer_req();
        void send_peer_data(); //send p2sp packet, may include data request, broadcast data
        double data_tx_wnd();
        int select_tx_chunk();
        virtual void process_data(int size, AppData* data);
        inline int p2sp_enable() {return p2sp_enable_;}
        void SlideOneChunk();
        void UpdateReqChunkBmp();
        void req_chunkmap();

        double wwan_rate_;
        double wlan_rate_;
        double playrate_;
        double chunk_interval_;

        strategy_t sel_scheme_;

        double peer_req_jitter_;
        double peer_data_jitter_;
        double srvr_data_timeout_;
        double peer_data_timeout_;

        int chunksize_;
        int bufsize_;
        int rd_wnd_;

        double prob_[CHKBUFSIZE];
        int timeslots_;
        int sel_;
        int buf_ready_;

        inline double get_chunk_interval() {return chunk_interval_;}


  protected:
        int command(int argc, const char*const* argv);
        void start();   //start sending or requesting data;
        void stop();    //stop requesting data?
        bool assemble(uint16_t chunkid);        //assemble downloaded chunks
        void rx_done();
        void dropchunk();
        bool hasChunk(uint16_t chunkid);
        void resetPendingBit(uint16_t chunkid);
        void updatePendingBmp(uint16_t, unsigned char *rq_bmp);

        void initbuf(uint16_t);

  private:

        virtual void recv_msg(int nbytes, const char *msg = 0);

        ReqTimer req_timer_;
        SrvrReqTimer srvr_req_timer_;
        DataTxTimer data_tx_timer_;
        ChunkMapReqTimer chunkmap_req_timer_;

        int chunk_map_size_;
        int filesize_;
        int running_;
        int nChunks_;


        //unsigned char req_bitmap[MAXBMPS];
        //uint16_t tx_Pending_[MAXBMPS*8];
        nsaddr_t srvr_id_;

        bool rx_done_;
        p2sp_state_t state_;
        peer_tx_t peer_tx_type_;

        int peer_down_;
        int srvr_down_;
        int peer_drop_;
        int srvr_drop_;
        int peer_up_;
        double max_tx_wnd_;
        int group_size_;
        int seqno_;

        int p2sp_enable_;
        double fetch_time_;
        double start_time_;


        StreamClientPlayTimer play_timer_;
        chunkbuf_t *head;
        chunkbuf_t *tail;


};

//P2SP Server Application Class Definition
class p2spStreamSrvr : public Application {
  public:
        p2spStreamSrvr();
        void send_p2sp_pkt(nsaddr_t,  int);
        void send_chunk_map(nsaddr_t);
        virtual void process_data(int size, AppData* data);
        double curplaytime;
        int curframe;
        void SlideOneChunk();
        inline double get_chunk_interval() {return chunk_interval_;}

        double playrate_;
        double chunk_interval_;
        strategy_t sel_scheme_;
        int chunksize_;
        int bufsize_;
        int sel_;
        double wwan_rate_;
        int rd_wnd_;


  protected:
        int command(int argc, const char*const* argv);
        void start();   //start p2sp Service, wait for data request
        void stop();    //stop p2sp Service
        void initbuf();
  private:
        void init();
        virtual void recv_msg(int nbytes, const char *msg = 0);

        int select_chunk(uint16_t, unsigned char*);
        int running_;

        int filesize_;
        int seqno_;
        int dbgno_;
        int nChunks_;
        int nframes_;
        char *stream_name_;


        uint16_t tx_Pending_[MAXBMPS*8];

        framelist_t frames_;
      //framelist_t frame_buf;

        StreamSrvrPlayTimer play_timer_;

        chunkbuf_t *head;
        chunkbuf_t *tail;

};

#endif /* P2SPSTREAM_H_ */
